#ifndef _BLOOMENTHAL_H_
#define _BLOOMENTHAL_H_

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include "ImplicitFunction.h"
#include "PolygonalMesh.h"

/*
 * C code from the article
 * "An Implicit Surface Polygonizer"
 * by Jules Bloomenthal, jbloom@beauty.gmu.edu
 * in "Graphics Gems IV", Academic Press, 1994
 */

/* implicit.c
 *     an implicit surface polygonizer, translated from Mesa
 *     applications should call polygonize()
 *
 * to compile a test program for ASCII output:
 *     cc implicit.c -o implicit -lm
 *
 * to compile a test program for display on an SGI workstation:
 *     cc -DSGIGFX implicit.c -o implicit -lgl_s -lm
 *
 * Authored by Jules Bloomenthal, Xerox PARC.
 * Copyright (c) Xerox Corporation, 1991.  All rights reserved.
 * Permission is granted to reproduce, use and distribute this code for
 * any and all purposes, provided that this notice appears in all copies. */

#define BLM_RES	10 /* # converge iterations    */

#define BLM_L	0  /* left direction:	-x, -i */
#define BLM_R	1  /* right direction:	+x, +i */
#define BLM_B	2  /* bottom direction: -y, -j */
#define BLM_T	3  /* top direction:	+y, +j */
#define BLM_N	4  /* near direction:	-z, -k */
#define BLM_F	5  /* far direction:	+z, +k */
#define BLM_LBN	0  /* left bottom near corner  */
#define BLM_LBF	1  /* left bottom far corner   */
#define BLM_LTN	2  /* left top near corner     */
#define BLM_LTF	3  /* left top far corner      */
#define BLM_RBN	4  /* right bottom near corner */
#define BLM_RBF	5  /* right bottom far corner  */
#define BLM_RTN	6  /* right top near corner    */
#define BLM_RTF	7  /* right top far corner     */

/* the BLM_LBN corner of cube (i, j, k), corresponds with location
 * (start.x+(i-.5)*size, start.y+(j-.5)*size, start.z+(k-.5)*size) */

#define BLM_RAND()	    ((rand()&32767)/32767.)    /* random number between 0 and 1 */
#define BLM_HASHBIT	    (5)
#define BLM_MASK	    ((1<<BLM_HASHBIT)-1)
#define BLM_HASHSIZE    (size_t)(1<<(3*BLM_HASHBIT))   /* hash table size (32768) */
#define BLM_HASH(i,j,k) ((((((i)&BLM_MASK)<<BLM_HASHBIT)|((j)&BLM_MASK))<<BLM_HASHBIT)|((k)&BLM_MASK))
#define BLM_BIT(i, bit) (((i)>>(bit))&1)

#define BLM_LB	0  /* left bottom edge	*/
#define BLM_LT	1  /* left top edge	*/
#define BLM_LN	2  /* left near edge	*/
#define BLM_LF	3  /* left far edge	*/
#define BLM_RB	4  /* right bottom edge */
#define BLM_RT	5  /* right top edge	*/
#define BLM_RN	6  /* right near edge	*/
#define BLM_RF	7  /* right far edge	*/
#define BLM_BN	8  /* bottom near edge	*/
#define BLM_BF	9  /* bottom far edge	*/
#define BLM_TN	10 /* top near edge	*/
#define BLM_TF	11 /* top far edge	*/

class Bloomenthal  
{
public:
  typedef struct point {		   /* a three-dimensional point */
      double x, y, z;		   /* its coordinates */
  } POINT1;

  typedef struct vertex {		   /* surface vertex */
      POINT1 position;//, normal;	   /* position and surface normal */
  } VERTEX;

  typedef struct vertices {	   /* list of vertices in polygonization */
      int count, max;		   /* # vertices, max # allowed */
      VERTEX *ptr;		   /* dynamically allocated */
  } VERTICES;

  typedef struct trilist {
    //int ci, cj, ck, cf;
	  int i1, i2, i3;
    bool b1, b2, b3;
	  struct trilist *next;
  } TRILIST;

  typedef struct cubelist {
    int ci, cj, ck;
    TRILIST *tris;
    int triscount;
	  struct cubelist *next;
  } CUBELIST;

public:
	Bloomenthal();
	virtual ~Bloomenthal();
  void setImplicitFunction(ImplicitFunction *f) { func = f; };
	int polygonize (VERTICES &vertices, CUBELIST* &cubelist, double &x, double &y, double &z, const double gsize);

private:
  typedef struct corner {		   /* corner of a cube */
      bool shared;
      int i, j, k;		   /* (i, j, k) is index within lattice */
      double x, y, z, value;	   /* location and function value */
  } CORNER;

  typedef struct cube {		   /* partitioning cell (cube) */
      bool good;
      int i, j, k;		   /* lattice location of cube */
      CORNER *corners[8];		   /* eight corners */
  } CUBE;

  typedef struct cubes {		   /* linked list of cubes acting as stack */
      CUBE cube;			   /* a single cube */
      struct cubes *next;		   /* remaining elements */
  } CUBES;

  typedef struct centerlist {	   /* list of cube locations */
      int i, j, k;		   /* cube location */
      struct centerlist *next;	   /* remaining elements */
  } CENTERLIST;

  typedef struct boundarylist {	   /* list of cube boundary locations */
      int i, j, k;		   /* cube location */
      struct boundarylist *next;	   /* remaining elements */
  } BOUNDARYLIST;

  typedef struct cornerlist {	   /* list of corners */
      int i, j, k;		   /* corner id */
      double value;		   /* corner value */
      struct cornerlist *next;	   /* remaining elements */
  } CORNERLIST;

  typedef struct edgelist {	   /* list of edges */
      int i1, j1, k1, i2, j2, k2;	   /* edge corner ids */
      int vid;			   /* vertex id */
      struct edgelist *next;	   /* remaining elements */
  } EDGELIST;

  typedef struct intlist {	   /* list of integers */
      int i;			   /* an integer */
      struct intlist *next;	   /* remaining elements */
  } INTLIST;

  typedef struct process {	   /* parameters, function, storage */
    double size;		   /* cube size */
	  POINT1 start;		   /* start point on surface */
    CUBES *cubes;		   /* active cubes */
    VERTICES vertices;		   /* surface vertices */
    CENTERLIST **centers;	   /* cube center hash table */
    CORNERLIST **corners;	   /* corner value hash table */
    EDGELIST **edges;		   /* edge and vertex id hash table */
  } PROCESS;

  typedef struct intlists {	   /* list of list of integers */
      INTLIST *list;		   /* a list of integers */
      struct intlists *next;	   /* remaining elements */
  } INTLISTS;

  enum FaceTest { GOOD_CUBE, BAD_CUBE, NO_CROSSING, OUT_OF_BOUNDS, GOOD_VISITED, BAD_VISITED };

	ImplicitFunction *func;

  static INTLISTS *cubetable[256];
  static int corner1[12];
  static int corner2[12];
  static int leftface[12];
  static int rightface[12];

private:
  void converge(const double x, const double y, double &zabove, double &zbelow, int iterations);
	void addtovertices (VERTICES *vertices, VERTEX v);
	int vertid (CORNER *c1, CORNER *c2, PROCESS *p);
	int getedge (EDGELIST *table[], int i1, int j1, int k1, int i2, int j2, int k2);
	void setedge (EDGELIST *table[], int i1, int j1, int k1, int i2, int j2, int k2, int vid);
	int setcenter(CENTERLIST *table[], int i, int j, int k);
	char * mycalloc (int nitems, int nbytes);
	void makecubetable ();
	int otherface (int edge, int face);
	int nextcwedge (int edge, int face);
	void docube(VERTICES &vertexlist, TRILIST* &trilist, int &tricount, CUBE* cube, PROCESS* p, bool boundary);
  bool find1(const int sign, const double gsize, const double x, const double y, double &z, double &value);
	CORNER * setcorner1(PROCESS* p, int i, int j, int k);
	FaceTest testface (int i, int j, int k, CUBE* old, int c1, int c2, int c3, int c4, PROCESS* p);
  void addcube(int i, int j, int k, PROCESS *p, bool good);
	void triproc (TRILIST* &trilist, int i1, int i2, int i3, bool b1, bool b2, bool b3);//int ci, int cj, int ck, int cf, 
};

#endif
